/****************************************************************************
 * Copyright (C) 2009-2010 SciTouch LLC
 * 
 * This file is part of Indigo toolkit.
 * 
 * This file may be distributed and/or modified under the terms of the
 * GNU General Public License version 3 as published by the Free Software
 * Foundation and appearing in the file LICENSE.GPL included in the
 * packaging of this file.
 * 
 * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
 * WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 ***************************************************************************/

#include "cano.h"
#include "base_cpp/tlscont.h"
#include "molecule/molecule.h"
#include "base_cpp/scanner.h"
#include "molecule/molecule_auto_loader.h"
#include "molecule/molecule_arom.h"
#include "base_cpp/output.h"
#include "molecule/canonical_smiles_saver.h"
#include "molecule/molecule_inchi.h"

class Cano
{
public:
   Array<char> message;
   Array<char> smiles;
   Molecule molecule;
   bool ignore_tetrahedral;
   bool ignore_cistrans;
   bool ignore_aromaticity;

   enum { SMILES_FORMAT, INCHI_FORMAT };

   Cano ()
   {
      ignore_tetrahedral = false;
      ignore_cistrans = false;
      ignore_aromaticity = false;
   }
};

TL_DECL(Cano, self);

#define CANO_BEGIN { TL_GET(Cano, self); try { self.message.clear();

#define CANO_END } catch (Exception &ex) \
      { self.message.readString(ex.message(), true); return 0; } \
      return 1; }

CEXPORT const char * canoGetVersion ()
{
   return "1.1-4507";
}

CEXPORT const char * canoGetLastError ()
{
   TL_GET(Cano, self);
   return self.message.ptr();
}

CEXPORT int canoSetOptions (const char *options)
{
   CANO_BEGIN
   {
      BufferScanner scanner(options);

      self.ignore_tetrahedral = false;
      self.ignore_cistrans = false;
      self.ignore_aromaticity = false;
      
      scanner.skipSpace();

      while (!scanner.isEOF())
      {
         QS_DEF(Array<char>, word);
         char c = scanner.readChar();

         if (c != '+' && c != '-')
            throw Exception("cannot parse options: '%s'", options);
         scanner.readWord(word, 0);

         if (strcasecmp(word.ptr(), "TETRAHEDRAL") == 0)
            self.ignore_tetrahedral = (c == '-');
         else if (strcasecmp(word.ptr(), "CISTRANS") == 0)
            self.ignore_cistrans = (c == '-');
         else if (strcasecmp(word.ptr(), "AROMATICITY") == 0)
            self.ignore_aromaticity = (c == '-');
         else
            throw Exception("cannot parse options: '%s'", options);

         scanner.skipSpace();
      }
   }
   CANO_END
}

CEXPORT int canoSetMolecule (const char *molecule)
{
   CANO_BEGIN
   {
      BufferScanner scanner(molecule);
      MoleculeAutoLoader loader(scanner);

      loader.ignore_stereocenter_errors = true;
      loader.loadMolecule(self.molecule, true);
   }
   CANO_END
}

int canoGetCanonicalCode (const char **code, int output_format)
{
   CANO_BEGIN
   {
      if (code == 0)
         throw Exception("zero string pointer is not allowed");

      self.molecule.calcImplicitHydrogens(true);
      self.molecule.findBondsInRings();
      if (!self.ignore_aromaticity && output_format != Cano::INCHI_FORMAT)
         MoleculeAromatizer::aromatizeBonds(self.molecule);

      if (self.ignore_cistrans)
         self.molecule.cis_trans.clear();

      if (self.ignore_tetrahedral)
         self.molecule.getStereocenters().clear();

      ArrayOutput output(self.smiles);

      if (output_format == Cano::SMILES_FORMAT)
      {
         CanonicalSmilesSaver saver(output);
         saver.saveMolecule(self.molecule);
      }
      else if (output_format == Cano::INCHI_FORMAT)
      {
         MoleculeInChI inchi_saver(output);
         inchi_saver.outputInChI(self.molecule);
      }
      else
         throw Exception("unknown output format %d", output_format);

      self.smiles.push(0);
      *code = self.smiles.ptr();
   }
   CANO_END
}

CEXPORT int canoGetCanonicalSmiles (const char **smiles)
{
   CANO_BEGIN
   {
      return canoGetCanonicalCode(smiles, Cano::SMILES_FORMAT);
   }
   CANO_END
}

CEXPORT int canoGetLayeredCode (const char **layered_code)
{
   CANO_BEGIN
   {
      return canoGetCanonicalCode(layered_code, Cano::INCHI_FORMAT);
   }
   CANO_END
}

CEXPORT qword canoAllocSessionId ()
{
   return TL_ALLOC_SESSION_ID();
}

CEXPORT void canoSetSessionId (qword id)
{
   TL_SET_SESSION_ID(id);
}

CEXPORT void canoReleaseSessionId (qword id)
{
   TL_RELEASE_SESSION_ID(id);
}
